(* Jeu "Ribkal le Nécromancien" *)

(** Bienvenue & règles du jeu *)

let introduction () =
print_endline "Vous incarnez un guerrier qui doit retrouver le sinistre enchanteur Ribkal.";
print_endline "Vous commencerez par créer votre personnage, puis à l'équiper dans l'armurerie,";
print_endline "avant de pénétrer dans le donjon de Ribkal.";
print_endline "Pour vous déplacer dans le donjon, vous utiliserez les 4 touches : ";
print_endline " * 'Z'   : avancer d'une case";
print_endline " * 'Q'   : tourner sur place d'un quart de tour vers la gauche";
print_endline " * 'D'   : tourner sur place d'un quart de tour vers la droite";
print_endline " * 'S'   : tourner sur place d'un demi tour.";
print_endline "Pour quitter la partie, appuyez sur la touche 'F'.";
print_newline ();
print_endline "A vous de jouer ... mais avant de démarrer, sachez que jusqu'à ce jour,";
print_endline "aucun aventurier n'est ressorti vivant de ce donjon maudit ... ";
print_endline " HAHAHAHAHAHAHAHAHAHAHA !!!!!!!";;

(** Races *)

type race = Humain | Zombie | Orque | Necromancien;;

let string_of_race = function
  Humain -> "Humain"
| Orque -> "Orque"
| Zombie -> "Zombie"
| Necromancien -> "Nécromancien";;

(** Armes *)

type arme = Desarme | Dague | Epee | Hache;;

let string_of_arme = function
  Desarme -> "Sans arme"
| Dague -> "Dague"
| Epee -> "Epee"
| Hache -> "Hache";;

let prix_arme = function
  Dague -> 10
| Epee -> 20
| Hache -> 30
| Desarme -> 0;;

let degats_arme = function
  Dague -> 20
| Epee -> 40
| Hache -> 60
| Desarme -> 5;;

(** Directions *)

type direction = Nord | Sud | Est | Ouest;;

let tourne_droite = function
  Nord -> Est
| Est -> Sud
| Sud -> Ouest
| Ouest -> Nord;;

let tourne_gauche = function
  Nord -> Ouest
| Ouest -> Sud
| Sud -> Est
| Est -> Nord;;

let demi_tour d =
   tourne_droite( tourne_droite d );;

let avance pos dir =
let x = fst pos
and y = snd pos in
match dir with
  Nord -> (x, y+1)
| Sud -> (x, y-1)
| Est -> (x+1, y)
| Ouest -> (x-1, y);;

(** Personnage *)

type personnage =
   { race : race; 
     mutable force : int; 
     mutable agilite : int; 
     mutable points_de_vie : int; 
     mutable pieces_d_or : int;
     mutable arme : arme;
     mutable pos : int*int;
     mutable dir : direction };;

let nouveau_personnage r f a pv po arme =
   { race = r; force = f; agilite = a; points_de_vie = pv; pieces_d_or = po;
     arme = arme; pos = (0,0); dir = Nord };;

let rec cree_un_personnage () =
   print_endline "Entrez le nombre de points d'agilite (>=5 et <=20): ";
   let a = read_int () in
   if ( ( a < 5 ) || ( a > 20 ) ) then
      begin
      print_endline "   valeur invalide ... recommencez !";
      cree_un_personnage ();
      end
   else
      let f = 25 - a
      and po = 35
      and pv = 20 in
      nouveau_personnage Humain f a pv po Desarme;;

let affiche_un_personnage perso =
   print_endline "================================";
   print_endline "Caracteristiques du personnage";
   print_endline ("   * Race          : " ^ string_of_race ( perso.race));
   print_endline ("   * Force         : " ^ string_of_int ( perso.force ));
   print_endline ("   * Agilite       : " ^ string_of_int ( perso.agilite ));
   print_endline ("   * Points de vie : " ^ string_of_int ( perso.points_de_vie ));
   print_endline ("   * Pieces d'or   : " ^ string_of_int ( perso.pieces_d_or ));
   print_endline ("   * Arme          : " ^ string_of_arme ( perso.arme ));
   print_endline "================================";;

(** PNJ *)



(** Armurerie *)
  
let rec achete_une_arme perso =
   let budget = perso.pieces_d_or in
   print_endline ("Vous etes dans l'armurerie. Vous disposez de " ^
                 (string_of_int budget) ^ " pieces d'or.");
   print_endline "Quelle arme choisissez-vous ?";
   print_endline (" * pour la dague qui coute " ^
                  string_of_int (prix_arme Dague) ^ " pieces d'or tapez 1");
   print_endline (" * pour l'epee qui coute " ^
                  string_of_int (prix_arme Epee) ^ " pieces d'or tapez 2");
   print_endline (" * pour la hache qui coute " ^
                  string_of_int (prix_arme Hache) ^ " pieces d'or tapez 3");
   print_endline (" * pour sortir sans rien acheter tapez 0");
   let choix = read_int () in
   match choix with
     1 -> let solde = budget - (prix_arme Dague) in
          if ( solde > 0 ) then
             begin
             perso.arme <- Dague;
             perso.pieces_d_or <- solde;
             end
          else
             begin
             print_endline "budget insuffisant ... recommencez !";
             achete_une_arme perso;
             end
   | 2 -> let solde = budget - (prix_arme Epee) in
          if ( solde > 0 ) then 
             begin
             perso.arme <- Epee;
             perso.pieces_d_or <- solde
             end
          else
             begin
             print_endline "budget insuffisant ... recommencez !";
             achete_une_arme perso
             end
   | 3 -> let solde = budget - (prix_arme Hache) in
          if ( solde > 0 ) then 
             begin
             perso.arme <- Hache;
             perso.pieces_d_or <- solde;
             end
          else
             begin
             print_endline "budget insuffisant ... recommencez !";
             achete_une_arme perso
             end
   | 0 -> print_endline "Au plaisir de vous revoir Messire"
   | _ -> print_endline "Je n'ai pas ceci en magasin ... recommencez !"; 
          achete_une_arme perso;;

(** Terrain *)

type case = Mur | Vide;;

let nx = 13 and ny = 14;;

let donjon =
   let t = Array.make_matrix nx ny Vide in
   (* mur Ouest *)
   Array.fill t.(0) 0 (ny-1) Mur;
   (* mur Est *)
   Array.fill t.(nx-1) 0 (ny-1) Mur;
   (* murs Nord & Sud *)
   for j = 1 to (nx-2) do
      t.(j).(0) <- Mur;
      t.(j).(ny-1) <- Mur;
   done;
   (* colonne par colonne pour le reste *)
   t.(1).(2) <- Mur; t.(1).(4) <- Mur; t.(1).(7) <- Mur; t.(1).(9) <- Mur;
   t.(2).(4) <- Mur;
   t.(3).(2) <- Mur; t.(3).(4) <- Mur; t.(3).(7) <- Mur; t.(3).(9) <- Mur;
   Array.fill t.(4) 1 (ny-2) Mur; t.(4).(3) <- Vide; t.(4).(10) <- Vide;
   t.(5).(7) <- Mur;
   t.(6).(1) <- Mur; t.(6).(2) <- Mur; t.(6).(3) <- Mur; t.(6).(4) <- Mur; 
                     t.(6).(5) <- Mur; t.(6).(7) <- Mur; t.(6).(9) <- Mur;
   t.(7).(2) <- Mur; t.(7).(5) <- Mur; t.(7).(7) <- Mur; t.(7).(8) <- Mur;
                     t.(7).(9) <- Mur; t.(7).(10) <- Mur; t.(7).(12) <- Mur;
   t.(8).(2) <- Mur; t.(8).(3) <- Mur; t.(8).(5) <- Mur; 
   t.(9).(5) <- Mur; t.(9).(6) <- Mur; t.(9).(7) <- Mur; t.(9).(8) <- Mur;
                     t.(9).(10) <- Mur; t.(9).(11) <- Mur; t.(9).(12) <- Mur;
   t.(10).(3) <- Mur;
   t.(11).(3) <-Mur; t.(11).(8) <- Mur;
   (* on perce la porte dans le mur Sud *)
   t.(5).(0) <- Vide;
   t;;

(* position de la porte d'entree *)
let entree () = (5,0);;

let valeur_case pos =
   let x = fst pos
   and y = snd pos in
   if ( ( ( x >= 0 ) && ( x < nx ) ) && 
        ( ( y >= 0 ) && ( y < ny ) ) ) then
      donjon.(x).(y)
   else
      Vide;;

let voisinage pos dir =
   let x = fst pos
   and y = snd pos in
   print_string ("x="^(string_of_int x)^" y="^(string_of_int y));
   print_newline ();
   let t = Array.make_matrix 3 3 Vide in
   for i = 0 to 2 do
      for j = 0 to 2 do
      let c = match dir with
        Nord  -> valeur_case ( x-1+i, y+j )
      | Sud   -> valeur_case ( x+1-i, y-j )
      | Est   -> valeur_case ( x+j, y+1-i )
      | Ouest -> valeur_case ( x-j, y-1+i ) 
      in t.(i).(j) <- c;
      done;
   done;
   t;;


(** Graphique *)
#load "graphics.cma";;

exception Erreur of string;;

(* couleurs *)

(* taille de la fenetre *)
let d = 50;;
let npx = (8*d + 1);;
let npy = npx;;

(* ecran a effacer *)
let efface_ecran () =
   let t = Array.make 4 (0,0) in
   t.(0) <- (0,0);
   t.(1) <- (0,npy-1);
   t.(2) <- (npx-1,npy-1);
   t.(3) <- (npx-1,0);
   Graphics.set_color Graphics.background;
   Graphics.fill_poly t;;
   
(* coin inferieur gauche de la fenetre *)
let coin_x = 500 and coin_y = 300;;

let lance_graphique () =
let config_fenetre = (" " ^ (string_of_int npx) ^ "x" ^ (string_of_int npy) ^ 
                      "+" ^ (string_of_int coin_x) ^ "-" ^ (string_of_int coin_y) ) in
Graphics.open_graph config_fenetre;
Graphics.auto_synchronize false;
Graphics.set_window_title "Ribkal le nécromancien";;

let dessine_mur i = 
let s =
   let t = Array.make 4 (0,0) in
   match i with
     0 -> t.(0) <- (3*d,3*d); t.(1) <- (3*d,5*d); t.(2) <- (2*d,6*d); t.(3) <- (2*d,2*d); t;
   | 1 -> t.(0) <- (5*d,3*d); t.(1) <- (6*d,2*d); t.(2) <- (6*d,6*d); t.(3) <- (5*d,5*d); t;
   | 2 -> t.(0) <- (0*d,2*d); t.(1) <- (2*d,2*d); t.(2) <- (2*d,6*d); t.(3) <- (0*d,6*d); t;
   | 3 -> t.(0) <- (2*d,2*d); t.(1) <- (6*d,2*d); t.(2) <- (6*d,6*d); t.(3) <- (2*d,6*d); t;
   | 4 -> t.(0) <- (6*d,2*d); t.(1) <- (6*d,6*d); t.(2) <- (8*d,6*d); t.(3) <- (8*d,2*d); t;
   | 5 -> t.(0) <- (1*d,1*d); t.(1) <- (2*d,2*d); t.(2) <- (2*d,6*d); t.(3) <- (1*d,7*d); t;
   | 6 -> t.(0) <- (6*d,2*d); t.(1) <- (6*d,6*d); t.(2) <- (7*d,7*d); t.(3) <- (7*d,1*d); t;
   | 7 -> t.(0) <- (0*d,1*d); t.(1) <- (1*d,1*d); t.(2) <- (1*d,7*d); t.(3) <- (0*d,7*d); t;
   | 8 -> t.(0) <- (1*d,1*d); t.(1) <- (7*d,1*d); t.(2) <- (7*d,7*d); t.(3) <- (1*d,7*d); t;
   | 9 -> t.(0) <- (7*d,1*d); t.(1) <- (8*d,1*d); t.(2) <- (8*d,7*d); t.(3) <- (7*d,7*d); t;
   | 10 -> t.(0) <- (0*d,0*d); t.(1) <- (1*d,1*d); t.(2) <- (1*d,7*d); t.(3) <- (0*d,8*d); t;
   | 11 -> t.(0) <- (7*d,1*d); t.(1) <- (8*d,0*d); t.(2) <- (8*d,8*d); t.(3) <- (7*d,7*d); t;
   | _ -> raise( Erreur "indice de mur trop grand" );
in 
   let col = match i with
     0 | 1 -> Graphics.rgb 192 192 192
   | 2 | 3 | 4 -> Graphics.rgb 160 160 160
   | 5 | 6 -> Graphics.rgb 128 128 128
   | 7 | 8 | 9 -> Graphics.rgb 96 96 96
   | 10 | 11 -> Graphics.rgb 64 64 64
   | _ -> raise( Erreur "indice de mur trop grand" );
in
   Graphics.set_color col;
   Graphics.fill_poly s;;

(* 0<=i<=2, 0<=j<=2 : represente l'une des 9 cases
   que peut voir le personnage *)
let dessine_case c i j =
match c with
  Vide -> ()
| Mur  -> begin
   match (i,j) with
     (0,2) -> dessine_mur 0; dessine_mur 2;
   | (1,2) -> dessine_mur 3;
   | (2,2) -> dessine_mur 1; dessine_mur 4;
   | (0,1) -> dessine_mur 5; dessine_mur 7;
   | (1,1) -> dessine_mur 8;
   | (2,1) -> dessine_mur 6; dessine_mur 9;
   | (0,0) -> dessine_mur 10;
   (* la case ou se trouve le personnage ne peut etre un Mur !!! *)
   | (1,0) -> raise (Erreur "le heros est dans un mur!!");
   | (2,0) -> dessine_mur 11;
   | (_,_) -> raise (Erreur "la case demandee est hors du champ de vision");
   end;;

let dessine_vois vois =
efface_ecran ();
for j = 2 downto 0 do
   dessine_case vois.(0).(j) 0 j;
   dessine_case vois.(2).(j) 2 j;
   dessine_case vois.(1).(j) 1 j;
done;
Graphics.synchronize ();;

(** Conclusion *)

let conclusion () =
Graphics.close_graph();
print_endline "Au revoir !";;

(** Boucle d'interaction *)

exception Fin;;

let interaction heros key =
   let (pos',dir') =
   match key with
     'z' -> (avance heros.pos heros.dir, heros.dir)
   | 'q' -> (heros.pos, tourne_gauche heros.dir)
   | 'd' -> (heros.pos, tourne_droite heros.dir)
   | 's' -> (heros.pos, demi_tour heros.dir)
   | 'f' -> raise Fin
   | _ -> (heros.pos, heros.dir)
   in
   match (valeur_case pos') with
     Mur -> print_endline "mur"; ()
   | Vide -> begin
        print_endline "vide";
        heros.pos <- pos'; 
        heros.dir <- dir'; 
        dessine_vois (voisinage heros.pos heros.dir);
        end;;

let jeu () =
begin
   introduction ();
   let heros = cree_un_personnage () in
   achete_une_arme heros;
   affiche_un_personnage heros;
   heros.pos <- entree ();
   heros.dir <- Nord;
   lance_graphique ();
   dessine_vois (voisinage heros.pos heros.dir);
   try
      while true do
         let evenement = Graphics.wait_next_event [Graphics.Key_pressed]
         in if evenement.Graphics.keypressed then interaction heros evenement.Graphics.key
      done
   with
      Fin -> conclusion ()
end;;


(** Tests *)

(*
lance_graphique ();;
dessine_vois (voisinage 5 2 Nord);;
Graphics.clear_graph();;
Graphics.synchronize();;
dessine_vois (voisinage 5 2 Est);;
Graphics.clear_graph();;
Graphics.synchronize();;
dessine_vois (voisinage 5 2 Ouest);;
Graphics.clear_graph();;
Graphics.synchronize();;
dessine_vois (voisinage 4 2 Sud);;
Graphics.clear_graph();;
Graphics.synchronize();;
*)
